#!/usr/bin/perl

#system("export NF2_DESIGN_DIR=/root/NF2/projects/packet_generator");

#folders
$root_folder="/root/switch_project";
$result_folder="$root_folder/experiment/exp26"; #root dir to store experimental resutls
$aggregated_result="$result_folder/total_result.txt"; #results for all experiments, used for plot

#input traffic file for netFPGA
$inpcap="$root_folder/traffic/G4/n1_to_e1_1500.pcap"; #flow1
$inpcap_flow2="$root_folder/traffic/G4/n2_to_e1_1500.pcap"; #flow2

$runs=1; # number of repeated experiment (same setting)
$iteration=100; #number of iteration for packet transmission in netFPGA
$iteration_flow2=$iteration; #number of iteration for packet transmission in netFPGA to dag2, need to ensure finish transmission after flow to dag1

#used to calculate gap (not used now)
$packet_size=1500; #byte
$number_of_packets=10*$iteration; #because nf2_dag1.pcap has 10 packets
@speed;
@gap;

#specify sending rate
$start_sending_rate=100;#Mb/s
$end_sending_rate=1000;
$sending_rate_gap=100;

#calculate speed and gap (gap is not used for now)
for($b=$start_sending_rate, $a=0; $b<=$end_sending_rate; $b+=$sending_rate_gap, $a++)
{
	$speed[$a]=$b;
	$gap[$a]=getInterFrameGap($speed[$a], $packet_size, $number_of_packets);
	print "\n speed = $speed[$a], gap = $gap[$a]\n";
}

#disable broadcast
system("dagflood -f $root_folder/traffic/G4/e1_to_n1.erf -d dag0 -c 1");

#run experiment
#j-control sending rate from netfpga to dag1, 
#i-control runs of experiments for same setting
#for($j=0; $j<=$#speed; $j++)
for($j=8; $j<=8; $j++)
{
	for($k=0; $k<=$#speed; $k++)
	#for($k=0; $k<=10; $k++)
	{
		for($i=1; $i<=$runs; $i++)
		{
			#create folder according to sending rate and index of run;
			mkdir "$result_folder/$speed[$j]M";
			mkdir "$result_folder/$speed[$j]M/$speed[$k]M";
			mkdir "$result_folder/$speed[$j]M/$speed[$k]M/$i";
			chdir "$result_folder/$speed[$j]M/$speed[$k]M/$i";

			system("dagsnap -d0:0 -o 1.erf &");
			system("dagsnap -d0:2 -o 2.erf &");
			#substract 1000 to avoid one rate is integer times of the other
			$rate = $speed[$j]*1000-1000;
			#$rate = $speed[$j]*1000;  
			$rate2 = $speed[$k]*1000;
			system("packet_generator.pl -q0 $inpcap -r0 $rate -i0 $iteration -q1 $inpcap_flow2 -r1 $rate2 -i1 $iteration_flow2 -q2 $inpcap -r2 $rate -i2 $iteration -q3 $inpcap_flow2 -r3 $rate2 -i3 $iteration_flow2");
			#clean up
			system("killall dagsnap");
			sleep 2;
		}
	}
}

#post-process data
#for($j=0; $j<=$#speed; $j++)
for($j=8; $j<=8; $j++)
{
	for($k=0; $k<=$#speed; $k++)
	#for($k=0; $k<=1; $k++)
	{
		for($i=1; $i<=$runs; $i++)
		{
			chdir "$result_folder/$speed[$j]M/$speed[$k]M/$i";
			print "Processing $result_folder/$speed[$j]M/$speed[$k]M/$i \n";
#processing erf
			system("dagbits -vf 1.erf decode |grep \'dst=\\|ts=\\|19 82 01 12 19 82 01 12\' > 1erf_grep.txt");
			system("dagbits -vf 2.erf decode |grep \'dst=\\|ts=\\|19 82 01 12 19 82 01 12\' > 2erf_grep.txt");

			simplify_grepped_erf_2flows("1erf_grep.txt", "flow_info.csv", "1_flow_info.csv", "2_flow_info.csv");
			simplify_grepped_erf_2flows("2erf_grep.txt", "ref_flow_info.csv", "1_ref_flow_info.csv", "2_ref_flow_info.csv");
			sleep(2);
			#compute_table("ref_flow_info.csv", "1_flow_info.csv", "2_flow_info.csv", "flow_table.csv");
			#compute_table_v2("flow_info.csv", "1_ref_flow_info.csv", "2_ref_flow_info.csv", "flow_table.csv");
			compute_table_v2("flow_info.csv", "1_ref_flow_info.csv", "2_ref_flow_info.csv", "flow_table.csv", "1_flow_table.csv", "2_flow_table.csv");
			system("sort flow_table.csv > flow_table_sorted.csv");
#clean up
			system("rm -f 1.erf");
			system("rm -f 2.erf");
			system("rm -f 1erf_grep.txt");
			system("rm -f 2erf_grep.txt");
			system("rm -f flow_table.csv");
			#system("rm -f flow_info.csv");
			#system("rm -f 1_flow_info.csv");
			#system("rm -f 2_flow_info.csv");
		}
	}
}

chdir "$root_folder/script";

#output format <flow#, seq#, timestamp#, total seq#>
# seq# is within 1-1000
sub simplify_grepped_erf_2flows
{
	my($in)=$_[0];
	my($out)=$_[1];
	my($out_f1)=$_[2];
	my($out_f2)=$_[3];
	open(IN, $in) or die "$in cannot be opened.\n";
	open(OUT, ">$out") or die "$out cannot be opened.\n";
	open(OUT_F1, ">$out_f1") or die "$out_f1 cannot be opened.\n";
	open(OUT_F2, ">$out_f2") or die "$out_f2 cannot be opened.\n";

	my($counter)=0;

	while(<IN>)
	{
		my($line)=$_;
		chomp($line);

		if($line =~ m/^(ts=)/) #timestamp
		{
			@block1=split(' ', $line);
			@block2=split(':', $block1[2]);
			$time = $block2[2]+$block2[1]*60+$block2[0]*3600;
		}	

		elsif($line =~ m/src=00:4e:46:32:43:00/) #src mac = flow1
		{
			#@block1=split(' ', $line);
			#$srcMac=$block1[1];
			$srcMac=1;
			#print "$block1[1]\n";
		}
		elsif($line =~ m/src=00:4e:46:32:43:01/) #src mac = flow2
		{
			#@block1=split(' ', $line);
			#$srcMac=$block1[1];
			$srcMac=2;
			#print "$block1[1]\n";
		}		
		elsif($line =~ m/(19 82 01 12 19 82 01 12)/) #seq number, and ready to output
		{
			@block1=split(' ', $line);
			$hex_value=$block1[0] . $block1[1];
			$dec_value=hex($hex_value);
			#print OUT "$dec_value\n";
			$counter=$counter+1;			
			
			print OUT "$srcMac, $dec_value, $time, $counter\n";

			if($srcMac==1)
			{
				print OUT_F1 "$dec_value, $time, $counter\n";
			}
			elsif($srcMac==2)
			{
				print OUT_F2 "$dec_value, $time, $counter\n";
			}
		}
		
	}

	close(IN);
	close(OUT);
	close(OUT_F1);
	close(OUT_F2);
}

#output format for total flow <trans_timestamp, seq#_total, flow#, seq#_per_flow, delay/loss, received_timestamp> //temp disabled
#output format for individual flow <trans_timestamp, flow#, seq#_per_flow, delay/loss, received_timestamp>
sub compute_table_v2
{
	my($in_flow)=$_[0];
	my($in_ref1)=$_[1];
	my($in_ref2)=$_[2];
	my($out)=$_[3];
	my($out_flow1)=$_[4];
	my($out_flow2)=$_[5];

	open(IN_FLOW, $in_flow) or die "$in_flow cannot be opened.\n";
	open(IN_REF1, $in_ref1) or die "$in_ref1 cannot be opened.\n";
	open(IN_REF2, $in_ref2) or die "$in_ref2 cannot be opened.\n";
	open(OUT, ">$out") or die "$out cannot be opened.\n";
	open(OUT_FLOW1, ">$out_flow1") or die "$out_flow1 cannot be opened.\n";
	open(OUT_FLOW2, ">$out_flow2") or die "$out_flow2 cannot be opened.\n";

	$is_ref1_finished=0;
	$is_ref2_finished=0;

	$total_seq=0;
	$flow1_seq=0;
	$flow2_seq=0;

	while($line_flow = <IN_FLOW>)
	{
		chomp($line_flow);
		#print("line_flow = $line_flow\n");
		
		@flow_block=split(', ', $line_flow);
		$flow_num_flow=$flow_block[0];
		$seq_flow=$flow_block[1];
		$timestamp_flow=$flow_block[2];
		$total_seq_flow=$flow_block[3];
		$formatted_timestamp = $prev_timestamp_flow; #in case of loss

		#print "flow: flow_num = $flow_num_flow, seq# = $seq_flow,  recv_timestamp = $timestamp_flow, total_seq# = $total_seq_flow\n";
		
		#process flow 1
		if($flow_num_flow == 1)
		{
			#find match in ref1 for current packet in flow
			while($line_ref1 = <IN_REF1>)
			{
				chomp($line_ref1);
				@ref1_block=split(', ', $line_ref1);
				$seq_ref1=$ref1_block[0];
				$timestamp_ref1=$ref1_block[1];
				$total_seq_ref1=$ref1_block[2];

				$total_seq = $total_seq+1;
				$flow1_seq = $flow1_seq+1;

				if($seq_ref1 == $seq_flow) #find match, then calculate delay
				{
					$delay = $timestamp_flow - $timestamp_ref1;
					$delay = $delay*(10**6); #convert to us
					$formatted_delay=sprintf("%.3f", $delay);
					print OUT "$timestamp_ref1, $total_seq, flow1, $flow1_seq, $formatted_delay, $timestamp_flow\n";
					print OUT_FLOW1 "$timestamp_ref1, flow1, $flow1_seq, $formatted_delay, $timestamp_flow\n";
					last;

				}
				else #loss
				{
					$formatted_delay = 0;
					print OUT "$timestamp_ref1, $total_seq, flow1, $flow1_seq, $formatted_delay, -1\n";
					print OUT_FLOW1 "$timestamp_ref1, flow1, $flow1_seq, $formatted_delay, -1\n";
				}
			}
		}
		elsif($flow_num_flow == 2) #process flow 2
		{	
			#find match in ref2 for current packet in flow
			while($line_ref2 = <IN_REF2>)
			{
				chomp($line_ref2);
				@ref2_block=split(', ', $line_ref2);
				$seq_ref2=$ref2_block[0];
				$timestamp_ref2=$ref2_block[1];
				$total_seq_ref2=$ref2_block[2];

				$total_seq = $total_seq+1;
				$flow2_seq = $flow2_seq+1;

				if($seq_ref2 == $seq_flow) #find match, then calculate delay
				{
					$delay = $timestamp_flow - $timestamp_ref2;
					$delay = $delay*(10**6); #convert to us
					$formatted_delay=sprintf("%.3f", $delay);
					print OUT "$timestamp_ref2, $total_seq, flow2, $flow2_seq, $formatted_delay, $timestamp_flow\n";
					print OUT_FLOW2 "$timestamp_ref2, flow2, $flow2_seq, $formatted_delay, $timestamp_flow\n";
					last;

				}
				else #loss
				{
					$formatted_delay = 0;
					print OUT "$timestamp_ref2, $total_seq, flow2, $flow2_seq, $formatted_delay, -1\n";
					print OUT_FLOW2 "$timestamp_ref2, flow2, $flow2_seq, $formatted_delay, -1\n";
				}
			}
		}

	}

	close(IN_FLOW);
	close(IN_REF1);
	close(IN_REF2);
	close(OUT_FLOW1);
	close(OUT_FLOW2);
	close(OUT);
}

#output format <flow#, seq#_2_flows, seq#, delay/loss, received_timestamp, rcv_frame_gap>
sub compute_table
{
	my($in_ref)=$_[0];
	my($in_flow1)=$_[1];
	my($in_flow2)=$_[2];
	my($out)=$_[3];

	open(IN_REF, $in_ref) or die "$in_ref cannot be opened.\n";
	open(IN_FLOW1, $in_flow1) or die "$in_flow1 cannot be opened.\n";
	open(IN_FLOW2, $in_flow2) or die "$in_flow2 cannot be opened.\n";
	open(OUT, ">$out") or die "$out cannot be opened.\n";

	$is_flow1_finished=0;
	$is_flow2_finished=0;
	
	$line_flow1 = <IN_FLOW1>;
	chomp($line_flow1);
	@flow1_block=split(', ', $line_flow1);
	$seq_flow1=$flow1_block[0];		
	$timestamp_flow1=$flow1_block[1];
	$total_seq_flow1=$flow1_block[2];

	#print "flow 1 beginning, seq_flow = $seq_flow1, timestamp_flow=$timestamp_flow1,  total_seq_flow=$total_seq_flow1\n";

	$line_flow2 = <IN_FLOW2>;
	chomp($line_flow2);
	@flow2_block=split(', ', $line_flow2);
	$seq_flow2=$flow2_block[0];		
	$timestamp_flow2=$flow2_block[1];
	$total_seq_flow2=$flow2_block[2];

	#print "flow 2 beginning, seq_flow = $seq_flow2, timestamp_flow=$timestamp_flow2,  total_seq_flow=$total_seq_flow2\n";

	while($line_ref = <IN_REF>)
	{
		chomp($line_ref);
		#print("line_ref = $line_ref\n");
		
		@ref_block=split(', ', $line_ref);
		$flow_num_ref=$ref_block[0];
		$seq_ref=$ref_block[1];
		$timestamp_ref=$ref_block[2];
		$total_seq_ref=$ref_block[3];

		#print "ref flow: flow_num = $flow_num_ref, seq_ref = $seq_ref,  timestamp_ref = $timestamp_ref, total_seq_ref=$total_seq_ref\n";

		if($flow_num_ref == 1) #process flow 1
		{

			if($seq_ref == $seq_flow1)#no loss
			{
				$delay = $timestamp_flow1 - $timestamp_ref;
				$delay = $delay*(10**6); #convert to us
				$formatted_delay=sprintf("%.3f", $delay);
				
				print OUT "flow$flow_num_ref, $total_seq_ref, $seq_flow1, $formatted_delay, $timestamp_flow1\n";
				#print "found a match, flow 1, total_seq_flow=$total_seq_ref, seq_flow = $seq_flow1, delay=$delay, recv_timestamp =$timestamp_flow1\n";

				#go to next record in flow
				$line_flow1 = <IN_FLOW1>;
				chomp($line_flow1);
				if($line_flow1!='') #last line can be empty
				{
					@flow1_block=split(', ', $line_flow1);
					$seq_flow1=$flow1_block[0];		
					$timestamp_flow1=$flow1_block[1];
					$total_seq_flow1=$flow1_block[2];
				#print "Next packet in flow 1, total_seq_flow=$total_seq_flow1, seq_flow = $seq_flow1, recv_timestamp =$timestamp_flow1\n";
				}
				else
				{
					$is_flow1_finished=1;
				}
			}
			else
			{
				$delay = 0; #loss
				print OUT "flow$flow_num_ref, $total_seq_ref, $seq_ref, $delay, -1\n";
				#print "No match, flow 1, total_seq_flow=$total_seq_ref, seq_flow = $seq_ref, delay=$delay, recv_timestamp =$timestamp_flow1\n";
			}
		}
		elsif($flow_num_ref == 2) #process flow 2
		{	

			if($seq_ref == $seq_flow2)#no loss
			{
				$delay = $timestamp_flow2 - $timestamp_ref;
				$delay = $delay*(10**6); #convert to us
				$formatted_delay=sprintf("%.3f", $delay);
				
				print OUT "flow$flow_num_ref, $total_seq_ref, $seq_flow2, $formatted_delay, $timestamp_flow2\n";
				#print "found a match, flow 2, total_seq_flow=$total_seq_ref, seq_flow = $seq_flow2, delay=$delay, recv_timestamp =$timestamp_flow2\n";

				#go to next record in flow
				$line_flow2 = <IN_FLOW2>;
				chomp($line_flow2);
				if($line_flow2!='') #last line can be empty
				{
					@flow2_block=split(', ', $line_flow2);
					$seq_flow2=$flow2_block[0];		
					$timestamp_flow2=$flow2_block[1];
					$total_seq_flow2=$flow2_block[2];
				#print "Next packet in flow 2, total_seq_flow=$total_seq_flow2, seq_flow = $seq_flow2, recv_timestamp =$timestamp_flow2\n";
				}
				else
				{
					$is_flow2_finished=1;
				}
			}
			else
			{
				$delay = 0; #loss
				print OUT "flow$flow_num_ref, $total_seq_ref, $seq_ref, $delay, -1\n";
				#print "No match, flow 2, total_seq_flow=$total_seq_, seq_flow = $seq_ref, delay=$delay, recv_timestamp =$timestamp_flow2\n";
			}
		}

		if($is_flow1_finished==1 && $is_flow2_finished==1)
		{
			last;
		}

	}

	close(IN_FLOW);
	close(IN_REF);
	close(OUT);
}

#arguments: 1. sending rate(Mb/s), 2. packet size(Byte) 3. number of packets
sub getInterFrameGap
{
	my($sending_rate)=$_[0];
	my($pk_size)=$_[1];
	my($num_of_packets)=$_[2];	
	#print("$sending_rate, $pk_size, $number_of_packets\n");

	my($bandwidth)=10**9;
	my($interframe_gap); #ns
	my($formatted_ifg);

	$interframe_gap=(($pk_size*8*$num_of_packets/$sending_rate/(10**6))-($pk_size*8/$bandwidth))/($num_of_packets-1) * (10**9);
	#print("interframe gap = $interframe_gap\n");
	
	$formatted_ifg=sprintf("%.0f", $interframe_gap);
	return $formatted_ifg;
}

#only up to day, be careful when doing experiment in midnight :)
#argument 1. input file after dagbits 2. output file
sub compute_timestamp
{
	my($in)=$_[0];
	my($out)=$_[1];

	open(IN, $in) or die "$in cannot be opened.\n";
	open(OUT, ">$out") or die "$out cannot be opened.\n";

	while(<IN>)
	{
		my($line)=$_;
		chomp($line);

		@block1=split(' ', $line);
		#print "$block1[2]\n";
		@block2=split(':', $block1[2]);
		#print "$block2[0] $block2[1] $block2[2]\n";
		$time = $block2[2]+$block2[1]*60+$block2[0]*3600;
		print OUT "$time\n";
		#print "$time\n";
	}

	close(IN);
	close(OUT);
}
